/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-02-27     chenbin      usb_app.c
 */
#include "n32g45x.h"
#include "usb_lib.h"
#include "usb_app.h"
#include "usb_app_pwr.h"
#include "usb_app_istr.h"

#include "usb_cdc_desc.h"

#include "rtthread.h"

#define USART_RX_DATA_SIZE   2048

uint8_t  USART_Rx_Buffer [USART_RX_DATA_SIZE]; 
uint32_t USART_Rx_ptr_in = 0;
uint32_t USART_Rx_ptr_out = 0;
uint32_t USART_Rx_length  = 0;
uint8_t  USB_Tx_State = 0;

void Enter_LowPowerMode(void)
{
    /* Set the device state to suspend */
    bDeviceState = SUSPENDED;
}

void Leave_LowPowerMode(void)
{
    USB_DeviceMess* pInfo = &Device_Info;

    /* Set the device state to the correct state */
    if (pInfo->CurrentConfiguration != 0)
    {
        /* Device configured */
        bDeviceState = CONFIGURED;
    }
    else
    {
        bDeviceState = ATTACHED;
    }
}

void USB_LP_CAN1_RX0_IRQHandler(void)
{
    usb_app_isr();
}

void USBWakeUp_IRQHandler(void)
{
    EXTI_ClrITPendBit(EXTI_LINE18);
}

void usb_app_hw_init(void)
{
	EXTI_InitType EXTI_InitStructure;
	NVIC_InitType NVIC_InitStructure;
	
	/* Select USBCLK source */
	RCC_ConfigUsbClk(RCC_USBCLK_SRC_PLLCLK_DIV3);
	/* Enable the USB clock */
	RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USB, ENABLE);
	
	/* Configure the EXTI line 18 connected internally to the USB IP */
	EXTI_ClrITPendBit(EXTI_LINE18);
	EXTI_InitStructure.EXTI_Line = EXTI_LINE18; 
	EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitPeripheral(&EXTI_InitStructure);
	
	/* 2 bit for pre-emption priority, 2 bits for subpriority */
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	/* Enable the USB interrupt */
	NVIC_InitStructure.NVIC_IRQChannel                   = USB_LP_CAN1_RX0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	/* Enable the USB Wake-up interrupt */
	NVIC_InitStructure.NVIC_IRQChannel                   = USBWakeUp_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	GPIO_InitType GPIO_InitStructure;

	RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);

	GPIO_InitStructure.Pin = GPIO_PIN_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
}


void usb_app_cdc_init(void)
{
    pInformation            = &Device_Info;
    pInformation->CtrlState = 2;
    pProperty               = &Device_Property;
    pUser_Standard_Requests = &User_Standard_Requests;
    /* Initialize devices one by one */
    pProperty->Init();
    /*Pull up DP*/
    _EnPortPullup();
}

void cdc_write_data(uint8_t *buffer, uint16_t len)
{
	USB_SilWrite(ENDP1, buffer, len);
//	USB_CopyUserToPMABuf(buffer, ENDP1_TXADDR, len);
//	USB_SetEpTxCnt(ENDP1, len);
	USB_SetEpTxValid(ENDP1); 
}

#include "rtthread.h"
#include "rtdevice.h"
#include "rthw.h"

#define CDC_EVENT_RX 0x01
#define CDC_EVENT_TX 0x02

#define CDC_TX_BUFSIZE 1024
#define CDC_RX_BUFSIZE 1024

struct cdc_vcom
{
	struct rt_device       device;
	rt_bool_t connected;
	rt_bool_t in_sending;
	rt_uint8_t rx_rbp[CDC_RX_BUFSIZE];
	struct rt_ringbuffer rx_ringbuffer;
	rt_uint8_t tx_rbp[CDC_TX_BUFSIZE];
	struct rt_ringbuffer tx_ringbuffer;
  struct rt_event      txrx_event;
	char*   name;
};

struct cdc_vcom n32_vcom = 
{
	.name = "vcom"
};


void cdc_rx_data_isr_process(uint8_t *buffer, uint16_t len)
{
	int rc = 0;
	rt_uint32_t level;
	level = rt_hw_interrupt_disable();
	rc = rt_ringbuffer_put_force(&n32_vcom.rx_ringbuffer, buffer, len);
	rt_hw_interrupt_enable(level);
	
	rc = rt_ringbuffer_data_len(&n32_vcom.rx_ringbuffer);
	if(rc > 0)
	{
		rt_event_send(&n32_vcom.txrx_event, CDC_EVENT_RX);
	}
}

void cdc_tx_data_isr_process(uint8_t *buffer, uint16_t len)
{
	
}


static rt_err_t rt_cdc_init(struct rt_device *dev)
{
	return RT_EOK;
}

static rt_err_t rt_cdc_open(struct rt_device *dev, rt_uint16_t oflag)
{
	return RT_EOK;
}

static rt_err_t rt_cdc_close(struct rt_device *dev)
{
	return RT_EOK;
}

static rt_size_t rt_cdc_read(struct rt_device *dev,  rt_off_t pos, void *buffer, rt_size_t size)
{
	int result;
	rt_uint32_t level;
	
	level = rt_hw_interrupt_disable();
	result = rt_ringbuffer_get(&n32_vcom.rx_ringbuffer, buffer, size);
	rt_hw_interrupt_enable(level);
	
	return result;
}

static rt_size_t rt_cdc_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
	int rc;
	int result;
	rt_uint32_t level;
	
	level = rt_hw_interrupt_disable();
	result = rt_ringbuffer_put_force(&n32_vcom.tx_ringbuffer, buffer, size);
	rt_hw_interrupt_enable(level);
	
	rc = rt_ringbuffer_data_len(&n32_vcom.tx_ringbuffer);
	if(rc > 0)
	{
		rt_event_send(&n32_vcom.txrx_event, CDC_EVENT_TX);
	}
	return result;
}


static rt_err_t rt_cdc_control(struct rt_device *dev, int cmd,  void *args)
{
	return RT_EOK;
}


/* Vcom Tx Thread */
static void vcom_tx_thread_entry(void* parameter)
{
	rt_uint32_t level;
	rt_uint32_t res;
	rt_uint32_t len;
	rt_uint8_t temp_buffer[256];

	while (1)
	{
		res = 0;
		rt_event_recv(&n32_vcom.txrx_event, CDC_EVENT_RX | CDC_EVENT_TX, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,  RT_WAITING_FOREVER, &res);
		if((res & CDC_EVENT_RX))
		{
			if(n32_vcom.device.rx_indicate != NULL)
			{
				len = rt_ringbuffer_data_len(&n32_vcom.rx_ringbuffer);
				rt_kprintf("cdc rx:%d\n",len);
				n32_vcom.device.rx_indicate(&(n32_vcom.device), len);
			}
		}
		if((res & CDC_EVENT_TX))
		{
			len = rt_ringbuffer_data_len(&n32_vcom.tx_ringbuffer);
			if(len > 0)
			{
				level = rt_hw_interrupt_disable();
				len = rt_ringbuffer_get(&n32_vcom.tx_ringbuffer, temp_buffer, sizeof(temp_buffer));
        rt_hw_interrupt_enable(level);
				rt_kprintf("cdc tx:%d\n",len);
				cdc_write_data(temp_buffer,len);
			}
		}
	}
}

#define VCOM_TASK_STK_SIZE  2048

static rt_uint8_t vcom_thread_stack[VCOM_TASK_STK_SIZE];
static struct rt_thread vcom_thread;

void usb_vcom_init(void)
{
	rt_err_t ret = RT_EOK;

	rt_event_init(&n32_vcom.txrx_event, n32_vcom.name, RT_IPC_FLAG_FIFO);
	/* initialize ring buffer */
	rt_ringbuffer_init(&n32_vcom.rx_ringbuffer, n32_vcom.rx_rbp, CDC_RX_BUFSIZE);
	rt_ringbuffer_init(&n32_vcom.tx_ringbuffer, n32_vcom.tx_rbp, CDC_TX_BUFSIZE);

	n32_vcom.device.type        = RT_Device_Class_Char;
	n32_vcom.device.rx_indicate = RT_NULL;
	n32_vcom.device.tx_complete = RT_NULL;
	n32_vcom.device.init        = rt_cdc_init;
	n32_vcom.device.open        = rt_cdc_open;
	n32_vcom.device.close       = rt_cdc_close;
	n32_vcom.device.read        = rt_cdc_read;
	n32_vcom.device.write       = rt_cdc_write;
	n32_vcom.device.control     = rt_cdc_control;

	/* register a character device */
	ret = rt_device_register(&(n32_vcom.device), n32_vcom.name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
	
	/* init usb device thread */
	rt_thread_init(&vcom_thread, "vcom",vcom_tx_thread_entry, &n32_vcom, vcom_thread_stack, VCOM_TASK_STK_SIZE,8, 20);
	ret = rt_thread_startup(&vcom_thread);
	RT_ASSERT(ret == RT_EOK);
}

void usb_app_init(void)
{
//	int key_state = 0;
//	int key_state_last = 0;
	usb_app_hw_init();
	usb_app_cdc_init();
	//USB_Init();
	rt_kprintf("usb init\n");
	while (bDeviceState != CONFIGURED)
	{
		rt_thread_mdelay(10);
	}
	rt_kprintf("usb loop\n");
	usb_vcom_init();
//	while(1)
//	{
//		rt_thread_mdelay(10);
//		
//		key_state = GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_0);
//		if(key_state_last != key_state)
//		{
//			key_state_last = key_state;
//			
//			rt_kprintf("key\n");
//			cdc_write_data("key\n",4);
//		}
//	};
}


